﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace VirtualControl
{
    public class VirtualBase
    {
        #region 静态方向
        static readonly Dictionary<Control, List<VirtualBase>> VirtralBases = new Dictionary<Control, List<VirtualBase>>();
        public static void VirtualPaint(Control parent, Graphics G)
        {
            if (VirtralBases.ContainsKey(parent))
            {
                foreach (var item in VirtralBases[parent]) item.Paint(G);
            }
        }

        public static List<VirtualBase> GetVirtual(Control parent)
        {
            if (VirtralBases.ContainsKey(parent)) return VirtralBases[parent];
            else return null;
        }
        #endregion

        #region 私有变量
        Size sizeParent;
        #endregion

        #region 公有属性
        Rectangle bounds = new Rectangle(0, 0, 20, 20);
        public Rectangle Bounds
        {
            get
            {
                if (this.AutoSize)
                {
                    this.bounds.Size = Size.Round(this.Parent.CreateGraphics().MeasureString(this.Text, this.Parent.Font));
                }
                return bounds; 
            }
            set
            {
                bounds = value;
                OnResize(null);
            }
        }

        AnchorStyles anchor = AnchorStyles.Left | AnchorStyles.Top;
        public AnchorStyles Anchor
        {
            get { return anchor; }
            set { anchor = value; }
        }

        public delegate bool VisibleCallback();
        public VisibleCallback VisibleBinding;
        bool visible = true;
        public bool Visible
        {
            get
            {
                if (VisibleBinding != null) this.visible = VisibleBinding();

                return this.visible;
            }
            set
            {
                if (VisibleBinding == null) this.visible = value;
            }
        }

        bool enabled = true;
        public delegate bool EnabledCallback();
        public EnabledCallback EnabledBinding;
        public bool Enabled
        {
            get
            {
                if (EnabledBinding != null) this.enabled = EnabledBinding();

                return this.enabled;
            }
            set
            {
                if (EnabledBinding == null) this.enabled = value;
            }
        }

        public readonly Control Parent;
        public delegate string TextCallback();
        public TextCallback TextBinding;
        string text = string.Empty;
        public string Text
        {
            get
            {
                if (TextBinding != null) this.text = TextBinding();

                return this.text;
            }
            set
            {
                if (TextBinding == null) this.text = value;
            }
        }

        public bool AutoSize { get; set; }

        public Color ForeColor = Color.Red;

        #endregion

        #region 控件构造
        public VirtualBase(Control parent)
        {
            if (parent == null) throw new Exception("Parent is null!");
            this.sizeParent = parent.Size;
            this.Parent = parent;
            this.Parent.MouseClick += new MouseEventHandler(Parent_MouseClick);
            this.Parent.MouseDoubleClick += new MouseEventHandler(Parent_MouseDoubleClick);
            this.Parent.MouseDown += new MouseEventHandler(Parent_MouseDown);
            this.Parent.MouseEnter += new EventHandler(Parent_MouseEnter);
            this.Parent.MouseLeave += new EventHandler(Parent_MouseLeave);
            this.Parent.MouseMove += new MouseEventHandler(Parent_MouseMove);
            this.Parent.MouseUp += new MouseEventHandler(Parent_MouseUp);
            this.Parent.MouseWheel += new MouseEventHandler(Parent_MouseWheel);
            this.Parent.Resize += new EventHandler(Parent_Resize);
            if (!VirtralBases.ContainsKey(parent))
                VirtralBases.Add(parent, new List<VirtualBase>());
            VirtralBases[parent].Add(this);
        }
        #endregion

        #region 窗体事件
        void Parent_Resize(object sender, EventArgs e)
        {
            switch (this.anchor)
            {
                case AnchorStyles.Top:
                case AnchorStyles.Top | AnchorStyles.Left:
                case AnchorStyles.Top | AnchorStyles.Right:
                case AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right:
                    break;
                case AnchorStyles.Bottom:
                case AnchorStyles.Bottom | AnchorStyles.Left:
                case AnchorStyles.Bottom | AnchorStyles.Right:
                case AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right:
                    this.bounds.Y += this.Parent.Height - this.sizeParent.Height;
                    break;
                case AnchorStyles.Top | AnchorStyles.Bottom:
                case AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left:
                case AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right:
                case AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right:
                    this.bounds.Height += this.Parent.Height - this.sizeParent.Height;
                    break;
                default:
                    if (this.sizeParent.Height != 0)
                        this.bounds.Y = this.bounds.Y * this.Parent.Height / this.sizeParent.Height;
                    break;
            }

            switch (this.anchor)
            {
                case AnchorStyles.Left:
                case AnchorStyles.Left | AnchorStyles.Top:
                case AnchorStyles.Left | AnchorStyles.Bottom:
                case AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom:
                    break;
                case AnchorStyles.Right:
                case AnchorStyles.Right | AnchorStyles.Top:
                case AnchorStyles.Right | AnchorStyles.Bottom:
                case AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom:
                    this.bounds.X += this.Parent.Width - this.sizeParent.Width;
                    break;
                case AnchorStyles.Left | AnchorStyles.Right:
                case AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top:
                case AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom:
                case AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom:
                    this.bounds.Width += this.Parent.Width - this.sizeParent.Width;
                    break;
                default:
                    if (this.sizeParent.Height != 0)
                        this.bounds.X = this.bounds.X * this.Parent.Width / this.sizeParent.Width;
                    break;
            }

            this.sizeParent = this.Parent.Size;

            OnResize(null);
        }
        protected virtual void OnResize(EventArgs e)
        {
            Invalidate();
        }

        /// <summary>
        /// 绘图区域无效，由外部决定是否重绘。
        /// </summary>
        public event EventHandler Invalidated;
        public void Invalidate()
        {
            if (this.Invalidated != null) Invalidated(this, null);
        }
        #endregion

        #region 鼠标事件
        void Parent_MouseClick(object sender, MouseEventArgs e)
        {
            if (this.Enabled && this.Visible && this.bounds.Contains(e.Location))
                OnMouseClick(new MouseEventArgs(e.Button, e.Clicks, e.X - this.bounds.X, e.Y - this.bounds.Y, e.Delta));
        }
        public event EventHandler<MouseEventArgs> MouseClick;
        protected virtual void OnMouseClick(MouseEventArgs e)
        {
            if (MouseClick != null) MouseClick(this, e);
            Invalidate();
        }

        void Parent_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (this.Enabled && this.Visible && this.bounds.Contains(e.Location))
                OnMouseDoubleClick(new MouseEventArgs(e.Button, e.Clicks, e.X - this.bounds.X, e.Y - this.bounds.Y, e.Delta));
        }
        public event EventHandler<MouseEventArgs> MouseDoubleClick;
        protected virtual void OnMouseDoubleClick(MouseEventArgs e)
        {
            if (MouseDoubleClick != null) MouseDoubleClick(this, e);
            Invalidate();
        }

        protected bool isParentMouseEnter = false;
        void Parent_MouseEnter(object sender, EventArgs e)
        {
            this.isParentMouseEnter = true;
            OnParentMouseEnter(e);
        }
        protected virtual void OnParentMouseEnter(EventArgs e)
        {
            Invalidate();
        }

        protected bool isMouseEnter = false;
        public event EventHandler<EventArgs> MouseEnter;
        protected virtual void OnMouseEnter(EventArgs e)
        {
            if (MouseEnter != null) MouseEnter(this, e);
            Invalidate();
        }

        void Parent_MouseDown(object sender, MouseEventArgs e)
        {
            if (this.Enabled && this.Visible && this.bounds.Contains(e.Location))
            {
                this.isMouseDown = true;
                OnMouseDown(new MouseEventArgs(e.Button, e.Clicks, e.X - this.bounds.X, e.Y - this.bounds.Y, e.Delta));
            }
        }
        protected bool isMouseDown = false;
        public event EventHandler<MouseEventArgs> MouseDown;
        protected virtual void OnMouseDown(MouseEventArgs e)
        {
            if (MouseDown != null) MouseDown(this, e);
            Invalidate();
        }

        void Parent_MouseMove(object sender, MouseEventArgs e)
        {
            if (this.Enabled && this.Visible && this.bounds.Contains(e.Location))
            {
                if (!this.isMouseEnter)
                {
                    this.isMouseEnter = true;
                    OnMouseEnter(null);
                }

                OnMouseMove(new MouseEventArgs(e.Button, e.Clicks, e.X - this.bounds.X, e.Y - this.bounds.Y, e.Delta));
            }
            else
            {
                if (this.isMouseEnter)
                {
                    this.isMouseEnter = false;
                    OnMouseLeave(null);
                }

                if (this.isMouseDown)
                {
                    OnMouseMove(new MouseEventArgs(e.Button, e.Clicks, e.X - this.bounds.X, e.Y - this.bounds.Y, e.Delta));
                }
            }
        }
        public event EventHandler<MouseEventArgs> MouseMove;
        protected virtual void OnMouseMove(MouseEventArgs e)
        {
            if (MouseMove != null) MouseMove(this, e);
            Invalidate();
        }

        void Parent_MouseUp(object sender, MouseEventArgs e)
        {
            if (this.isMouseDown)
            {
                this.isMouseDown = false;

                //if (this.Enabled && this.Visible)
                //{
                    OnMouseUp(new MouseEventArgs(e.Button, e.Clicks, e.X - this.bounds.X, e.Y - this.bounds.Y, e.Delta));
                //}
            }
        }
        public event EventHandler<MouseEventArgs> MouseUp;
        protected virtual void OnMouseUp(MouseEventArgs e)
        {
            if (MouseUp != null) MouseUp(this, e);
            Invalidate();
        }

        void Parent_MouseLeave(object sender, EventArgs e)
        {
            this.isParentMouseEnter = false;
            OnParentMouseLeave(e);
            if (this.Enabled && this.Visible  && this.isMouseEnter)
            {
                isMouseEnter = false;
                OnMouseLeave(null);
            }
        }
        protected virtual void OnParentMouseLeave(EventArgs e)
        {
            Invalidate();

        }
        public event EventHandler<EventArgs> MouseLeave;
        protected virtual void OnMouseLeave(EventArgs e)
        {
            if (MouseLeave != null) MouseLeave(this, null);
            Invalidate();
        }

        void Parent_MouseWheel(object sender, MouseEventArgs e)
        {
            if (this.Enabled && this.Visible && this.bounds.Contains(e.Location))
                OnMouseWheel(new MouseEventArgs(e.Button, e.Clicks, e.X - this.bounds.X, e.Y - this.bounds.Y, e.Delta));
        }
        public event EventHandler<MouseEventArgs> MouseWheel;
        protected virtual void OnMouseWheel(MouseEventArgs e)
        {
            if (MouseWheel != null) MouseWheel(this, e);
            Invalidate();
        }
        #endregion

        #region 控件绘图
        /// <summary>
        /// 由外部重绘
        /// </summary>
        /// <param name="G"></param>
        public void Paint(Graphics G)
        {
            if (this.Visible)
            {
                Region regionTemp = G.Clip;
                G.Clip = new Region(this.bounds);
                OnPaint(G);
                G.Clip = regionTemp;
            }
        }

        protected virtual void OnPaint(Graphics G)
        {
            DrawRectangle(G, Pens.Red, this.Bounds);
            using (StringFormat SF = new StringFormat())
            {
                SF.Alignment = StringAlignment.Center;
                SF.LineAlignment = StringAlignment.Center;
                G.DrawString(this.Text, this.Parent.Font, new SolidBrush(this.ForeColor), this.Bounds, SF);
            }
        }

        /// <summary>
        /// 用四条线段代替直接绘制矩形
        /// </summary>
        /// <param name="G"></param>
        /// <param name="pen"></param>
        /// <param name="rect"></param>
        public static void DrawRectangle(Graphics G, Pen pen, Rectangle rect)
        {
            Point[] vertexes = new Point[]{
                        new Point(rect.Left,rect.Top),
                        new Point(rect.Right - 1, rect.Top),
                        new Point(rect.Right - 1, rect.Bottom - 1),
                        new Point(rect.Left,rect.Bottom - 1)
                    };
            G.DrawLine(pen, vertexes[0], vertexes[1]);
            G.DrawLine(pen, vertexes[1], vertexes[2]);
            G.DrawLine(pen, vertexes[2], vertexes[3]);
            G.DrawLine(pen, vertexes[3], vertexes[0]);
        }
        #endregion
    }
}
